#ifdef __KERNEL__
#include <linux/string.h>
#else
#include <string.h>
#endif
#include "modes_local.h"

#if !defined(STRICT_ALIGNMENT) && !defined(PEDANTIC)
#define STRICT_ALIGNMENT 0
#endif

#if defined(__GNUC__) && !STRICT_ALIGNMENT
typedef size_t size_t_aX __attribute((__aligned__(1)));
#else
typedef size_t size_t_aX;
#endif

void CRYPTO_cbc128_encrypt(const unsigned char* in, unsigned char* out,
                           size_t len, const void* key, unsigned char ivec[16],
                           block128_f block)
{
    size_t               n;
    const unsigned char* iv = ivec;

    if (len == 0) return;

    while (len) {
        for (n = 0; n < 16 && n < len; ++n) out[n] = in[n] ^ iv[n];
        for (; n < 16; ++n) out[n] = iv[n];
        (*block)(out, out, key);
        iv = out;
        if (len <= 16) break;
        len -= 16;
        in += 16;
        out += 16;
    }
    if (ivec != iv) memcpy(ivec, iv, 16);
}

void CRYPTO_cbc128_decrypt(const unsigned char* in, unsigned char* out,
                           size_t len, const void* key, unsigned char ivec[16],
                           block128_f block)
{
    size_t n;
    union {
        size_t        t[16 / sizeof(size_t)];
        unsigned char c[16];
    } tmp;

    if (len == 0) return;

    while (len) {
        unsigned char c;
        (*block)(in, tmp.c, key);
        for (n = 0; n < 16 && n < len; ++n) {
            c       = in[n];
            out[n]  = tmp.c[n] ^ ivec[n];
            ivec[n] = c;
        }
        if (len <= 16) {
            for (; n < 16; ++n) ivec[n] = in[n];
            break;
        }
        len -= 16;
        in += 16;
        out += 16;
    }
}
